
The R Ecosystem
This course will use R for model exploration, data
analysis, and writing results. If you do not have R on your
laptop, please download it from here and install the appropriate
version for your platform.
R Version
I am currently using R version 4.3.3 for this document.
To check the version of your R instance, type:
version
If the major version of your install is 4, you should be fine. If it
is earlier, you should upgrade. If the first number in the minor version
is lower than this, you should also consider upgrading. The minor “dot
version” is not as important.
Upgrading from a Previous Version
R stores all the libraries in a version-specific folder
structure. Before you upgrade, you may want to make a backup of
which sets of libraries/packages you have installed already so that when
you upgrade, you can automatically reinstall the packages into the new
system. Here is how you’d do that.
- Make a record of the packages you already have installed. This takes
the list of packages you have, pulls out the names of each package and
saves it as an R data file (
.rda) on your Desktop. After
this, you can close R.
pkgs <- installed.packages()
pkgs <- names( is.na(pkgs[,4]))
save(pkgs,file='~/Desktop/pkgs.rda')
- Download and Install the new version of
R.
If you are on Windows… I’m sorry, I feel for you. Make sure you
install the UCRT package that is in the base
install documents. There may be other issues that come up, I am not
familiar with this platform but in almost all the workshops I lead,
there are Windows issues.
- Find the packages you need to install by comparing it to what you
had previously and install them all at once.
new_pkgs <- installed.packages()
new_pkgs <- names( is.na(new_pkgs[,4]))
load("~/Desktop/pkgs.rda")
to_install <- setdiff( pkgs, new_pkgs )
install.packages( to_install )
update.packages( ask=FALSE )
Updating your current set of packages.
If you just updated R, you can skip this section. If you
have not updated your package list in a while, you may want to update
any out-of-date packages you may have on your system. Do this when you
are not needing to use R for a while, depending upon how
much you need to upgrade, it may take a while.
update.packages( ask=FALSE )
If you do not put the ask=FALSE part in there, you’ll be
tethered to the computer because it will ask your permission to install
each package one at a time…
RStudio
I am assuming that you are using RStudio for this course. If you prefer to
use another platform such as Jupyter
Notebooks, Visual
Studio, or other IDE’s, you are on your own. However, this course
will require you to produce markdown documents in HTML or DOCX
format.
If you do not have the most recent version of RStudio, please
download it from here
Quarto
We are going to use Quarto in this
course to make integrate our data analysis, graphical output, report
writing. If you have not used quarto before, you will need
to download the most recent version. There is a complete discussion of
how to start with Quarto on the website. Download the version for your
compute platform and choose RStudio (or whatever IDE) you
are using. It will set you up.
RStudio Projects
RStudio has the concept of a project for code
and data organization. I highly recommend that you use a single
project for this course. This will co-locate all the data, all the code,
and all the markdown that you will be given and that you create for this
class. It will also seemlessly handle working directories and all the
other crap that befuddles many students. To make a new package, open up
RStudio and do the following:
From the File menu, select New Projct

Select New Directory from the popup dialog. This
will create a new folder to put all your materials into.

- Select
New Project for this, we are going to create a
generic project to put all the course materials into.

- Create a name for the folder, perhaps select the name of the class
or some other designation that works with your file organization system.
Also, put it somewhere that will be useable (I had a student recently
who only put stuff in the
Downloads folder for some
reason…).

- Select
Create Project.
Now, whenever you get a document or data file, put it in this
folder.
It is important for me to emphasize this. When you download a data
set or markdown document or R script and simply double click on it, it
will most likely be in your Downloads folder.
Move it to the project folder so it is co-located
with all the other stuff for this course. If you do not, some of the
code may not work properly and you’ll be hating yourself. (Dyer’s #1
rule, do things now so ‘future self’ does not hate decisions that ‘past
self’ made…)
More importantly, when RStudio opens, it will default to
opening the most recent project (which in all likelihood will be this
project).
Markdown
In R, we have two categories of documents that we will
be using.
R Scripts
You can write pure R code in scripts, which are files
that are executed one line at a time, identical to you typing it into
the R console. These files are saved with the suffix
.R. These are great for pure data manipulation and
coding.
To create an R Script select File\(\to\)New File\(\to\)R Script.

To execute R code in a script, we can select
Source or Run from the button at top of the
RStudio editor window (just above the script itself). Or,
you can execute the code in a script from another location (e.g., from
another script) using the function source() and passing it
the path (relative to the document you are using right now) to the
script. It will then grab the code from the script and execute it as if
it was part of this script.
source("path_to/my_cool_script.R")
This is very helpful, particularly if you routinely get raw data that
needs some preliminary QA/QC, you can set up a script that will clean it
up and call it as necessary.
It is important to think about compartmenatalization of your code. If
you ever find yourself typing or (worse yet) copy-and-pasting code
you’ve used before into a new analysis—STOP. One of our goals is to
write as few lines of code as possible. Errors (logical and
grammatical) increase linearly with the number of lines of code you
write. The Dont Repeat Yourself
(DRY) principle is key to success in data analytics. Refactor
things for reuse will always be benefitical to you in the long run.
One last thing. When you write code in a script, use a lot of
comments to explain what you are doing in the code. Here is the syntax
of a single line comment.
# This is a comment and will not be interpreted by R
‘Future self’ will thank you when you come back to this file and try
to figure out how to use it again.
Quarto Documents
The next kind of file we will be working with are quarto markdown
documents. These documents let you mix together the R code
(like the scripts), graphical output, tabular output, and the larger
narratives around data and analyses you are creating. It is also
possible to link the numerical values assocaited with an analysis to
in-text values so that if the data change the text changes as well.
Figure and table numbering and citations are also updated automatically.
It is in your best interest (if your time is important to you) to become
proficient in how markdown and R interact.
The document you are reading right here is a quarto document. These
files end in the file suffix .qmd.
If you have not used markdown before, you need to jump in right now.
Here are some resources for you from a Data Literacy course I teach
which will help you get up-to-speed on using Markdown in RStudio.
- Slides
describing the rationale for Markdown and how to use it in RStudio where
you can mix textual content with data, code, and output.
- A longer narrative
on the topic that goes into more depth than the slides that includes
code and example output.
- A Video on Markdown and
Notebooks in RStudio that I made during the COVID lockdown when teaching
this course.
- External resources realted to using markdown:
Data Maniuplation
If we look at the amount of time we allocate to our data analytics,
the vast majority of it is spent on working on the data, manipulating
it, getting it into a format that is usable, and a very small fraction
of it is spent on running an actual analysis. Usually, by the time we
get to the analysis, we are doing a simple function call with our
formatted data.
analysis( myData )
I am hoping that you are versed in using tidyverse for
data manipulation. This is a constellation of packages that help you
take raw data (genotypes, rasters, shapefiles, csv files, etc.) and
perform operations on it. Loading in tidyverse is done by
calling the library directly.
library( tidyverse )
If you are getting a “library not found” for this, install it using
the following.
install.packages( "tidyverse" )
Here is an example of how it can be used to summarize data from the
built-in Iris data set.
summary( iris )
The following code does the following:
- outputs the raw data. - creates a new column of data with genus and
species. - groups by species. - summarizes by taking the mean values for
petal length and width. - saves as a data.frame to a local
variable.
iris |>
mutate( Plant = paste( "Iris",Species)) |>
group_by( Plant ) |>
summarize( Length = mean( Petal.Length ),
Width = mean( Petal.Width ) ) -> iris_summary
And this is what the output looks like.
iris_summary
If you were unsure what the output of the code was going to look like
or do not understand what was done, you may need to work a bit on
getting up to speed on tidyverse.
Here are some resources on using tidyverse from a few
different sources.
- A Video
introducing
tidyverse given from a distributed workshop on
Landscape Genetics.
- Updated Slides
of the content. These are updated from the set in the video as they are
the ones I used last semseter.
- A larger narrative
going into more depth on the specifics of the topic.
- The data set we will use for this is the Rice Rivers Center data set
(from 2014). You can see it here as its normal Google
Sheets or as a CSV
file.
- A cheat
sheet going over the basic analysis verb functions from
dplyr.
Graphics
Part of the tidyverse is the ggplot2
library. This is the primary and dominant graphing output for
R and will be used throughout this course. You should be
familiar with the code sets below (enough to understand what they are
doing) and be able to produce basic data graphics.
A basic scatter plot with groupings and a trend line (@fig-scatter).
iris |>
ggplot( aes(Petal.Width, Petal.Length ) ) +
stat_smooth( formula = y~x,
method="lm",
se=FALSE, col="gray",
lwd=0.5, lty=2 ) +
geom_point( aes( color = Species ) ) +
xlab("Petal Width (mm)") +
ylab("Petal Length (mm)")
A categorical data output using boxplot() (@fig-boxplot) or violin() (@fig-violin) display to compare continuous
responses from factors.
iris |>
ggplot( aes(Species,Petal.Length) ) +
geom_boxplot(notch=TRUE) +
xlab("Iris species") +
ylab("Petal Length (mm)")
iris |>
ggplot( aes(Species,Petal.Length) ) +
geom_violin() +
geom_jitter( width = 0.05, alpha = 0.5) +
xlab("Iris species") +
ylab("Petal Length (mm)")
A representation of the distribution of variables (@fig-density) across different groups.
iris |>
ggplot( aes(Sepal.Length, fill=Species) ) +
geom_density( alpha = 0.75, col="darkgray") +
xlab("Iris species") +
ylab("Petal Length (mm)") +
xlim( c(3,9) )
If these methods are new to you, here are some resources that will
help.
Tabular Output
The last part of this review will focus on tabular output. Many
times, we have data we need to summarize (see above the
tidyverse example data that produced a
data.frame of mean petal sizes) and would like to create a
table in our document that will present the results to the reader (or an
ANOVA table or whatever). The most common approach in R is
to use the following:
library( knitr )
library( kableExtra )
Here is the data from above:
iris_summary
We can make this into a table for publication output as is (@tbl-iris).
iris_summary |>
kable() |>
kable_styling( bootstrap_options = c("striped", "hover", "condensed", "responsive") ) |>
add_header_above(c(" " = 1, "Mean Petal Measurement (mm)"=2) ) |>
footnote(general="N = 50 per species.", footnote_as_chunk = TRUE)
There is a great webpage for kableExtra that describes a
lot of additional functionality for this library here.
I recommend you take a look at it.
Conclusion
So this should be sufficient for what we need in this course. If you
have any concerns about your ability to do any of the above, please let
me know and we can get to work on helping you get to where you need to
be.
If you have any questions, feel free to drop by my office (LFSC
105c), jump onto my Office Hours Zoom (linked form Canvas), or send me an email.
LS0tCnRpdGxlOiAiUiBFY29zeXN0ZW0gUmV2aWV3IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiFbXShtZWRpYS9HZW5lcmF0ZWRJbWFnZS5wbmcpCgojIFRoZSBgUmAgRWNvc3lzdGVtCgpUaGlzIGNvdXJzZSB3aWxsIHVzZSBgUmAgZm9yIG1vZGVsIGV4cGxvcmF0aW9uLCBkYXRhIGFuYWx5c2lzLCBhbmQgd3JpdGluZyByZXN1bHRzLiAgSWYgeW91IGRvIG5vdCBoYXZlIGBSYCBvbiB5b3VyIGxhcHRvcCwgcGxlYXNlIGRvd25sb2FkIGl0IGZyb20gW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnKSBhbmQgaW5zdGFsbCB0aGUgYXBwcm9wcmlhdGUgdmVyc2lvbiBmb3IgeW91ciBwbGF0Zm9ybS4KCgojIyMgUiBWZXJzaW9uCgo6Ojp7LmNhbGxvdXQtaW5mb30KSSBhbSBjdXJyZW50bHkgdXNpbmcgUiB2ZXJzaW9uIGByIHBhc3RlKCB2ZXJzaW9uJG1ham9yLCB2ZXJzaW9uJG1pbm9yLCBzZXA9Ii4iKWAgZm9yIHRoaXMgZG9jdW1lbnQuICAKOjo6CgpUbyBjaGVjayB0aGUgdmVyc2lvbiBvZiB5b3VyIGBSYCBpbnN0YW5jZSwgdHlwZToKCmBgYHtyfQp2ZXJzaW9uCmBgYAoKSWYgdGhlIG1ham9yIHZlcnNpb24gb2YgeW91ciBpbnN0YWxsIGlzIGByIHZlcnNpb24kbWFqb3JgLCB5b3Ugc2hvdWxkIGJlIGZpbmUuICBJZiBpdCBpcyBlYXJsaWVyLCB5b3Ugc2hvdWxkIHVwZ3JhZGUuICBJZiB0aGUgZmlyc3QgbnVtYmVyIGluIHRoZSBtaW5vciB2ZXJzaW9uIGlzIGxvd2VyIHRoYW4gdGhpcywgeW91IHNob3VsZCBhbHNvIGNvbnNpZGVyIHVwZ3JhZGluZy4gIFRoZSBtaW5vciAiZG90IHZlcnNpb24iIGlzIG5vdCBhcyBpbXBvcnRhbnQuCgojIyMjIFVwZ3JhZGluZyBmcm9tIGEgUHJldmlvdXMgVmVyc2lvbgoKYFJgIHN0b3JlcyBhbGwgdGhlIGxpYnJhcmllcyBpbiBhIHZlcnNpb24tc3BlY2lmaWMgZm9sZGVyIHN0cnVjdHVyZS4gICpCZWZvcmUqIHlvdSB1cGdyYWRlLCB5b3UgbWF5IHdhbnQgdG8gbWFrZSBhIGJhY2t1cCBvZiB3aGljaCBzZXRzIG9mIGxpYnJhcmllcy9wYWNrYWdlcyB5b3UgaGF2ZSBpbnN0YWxsZWQgYWxyZWFkeSBzbyB0aGF0IHdoZW4geW91IHVwZ3JhZGUsIHlvdSBjYW4gYXV0b21hdGljYWxseSByZWluc3RhbGwgdGhlIHBhY2thZ2VzIGludG8gdGhlIG5ldyBzeXN0ZW0uICBIZXJlIGlzIGhvdyB5b3UnZCBkbyB0aGF0LgoKMS4gTWFrZSBhIHJlY29yZCBvZiB0aGUgcGFja2FnZXMgeW91IGFscmVhZHkgaGF2ZSBpbnN0YWxsZWQuICBUaGlzIHRha2VzIHRoZSBsaXN0IG9mIHBhY2thZ2VzIHlvdSBoYXZlLCBwdWxscyBvdXQgdGhlIG5hbWVzIG9mIGVhY2ggcGFja2FnZSBhbmQgc2F2ZXMgaXQgYXMgYW4gUiBkYXRhIGZpbGUgKGAucmRhYCkgb24geW91ciBEZXNrdG9wLiAgQWZ0ZXIgdGhpcywgeW91IGNhbiBjbG9zZSBSLgoKYGBge3J9CiN8IGV2YWw6IGZhbHNlCnBrZ3MgPC0gaW5zdGFsbGVkLnBhY2thZ2VzKCkKcGtncyA8LSBuYW1lcyggaXMubmEocGtnc1ssNF0pKQpzYXZlKHBrZ3MsZmlsZT0nfi9EZXNrdG9wL3BrZ3MucmRhJykKYGBgCgoyLiBEb3dubG9hZCBhbmQgSW5zdGFsbCB0aGUgbmV3IHZlcnNpb24gb2YgYFJgLiAgCgo6Ojp7LmNhbGxvdXQtd2FybmluZ30KSWYgeW91IGFyZSBvbiBXaW5kb3dzLi4uIEknbSBzb3JyeSwgSSBmZWVsIGZvciB5b3UuICBNYWtlIHN1cmUgeW91IGluc3RhbGwgdGhlIGBVQ1JUYCBwYWNrYWdlIHRoYXQgaXMgaW4gdGhlIGBiYXNlYCBpbnN0YWxsIGRvY3VtZW50cy4gIFRoZXJlIG1heSBiZSBvdGhlciBpc3N1ZXMgdGhhdCBjb21lIHVwLCBJIGFtIG5vdCBmYW1pbGlhciB3aXRoIHRoaXMgcGxhdGZvcm0gYnV0IGluIGFsbW9zdCBhbGwgdGhlIHdvcmtzaG9wcyBJIGxlYWQsIHRoZXJlIGFyZSBXaW5kb3dzIGlzc3Vlcy4KOjo6CgozLiBGaW5kIHRoZSBwYWNrYWdlcyB5b3UgbmVlZCB0byBpbnN0YWxsIGJ5IGNvbXBhcmluZyBpdCB0byB3aGF0IHlvdSBoYWQgcHJldmlvdXNseSBhbmQgaW5zdGFsbCB0aGVtIGFsbCBhdCBvbmNlLgoKCmBgYHtyfQojfCBldmFsOiBmYWxzZQpuZXdfcGtncyA8LSBpbnN0YWxsZWQucGFja2FnZXMoKQpuZXdfcGtncyA8LSBuYW1lcyggaXMubmEobmV3X3BrZ3NbLDRdKSkKbG9hZCgifi9EZXNrdG9wL3BrZ3MucmRhIikKdG9faW5zdGFsbCA8LSBzZXRkaWZmKCBwa2dzLCBuZXdfcGtncyApCmluc3RhbGwucGFja2FnZXMoIHRvX2luc3RhbGwgKQp1cGRhdGUucGFja2FnZXMoIGFzaz1GQUxTRSApCmBgYAoKCiMjIyMgVXBkYXRpbmcgeW91ciBjdXJyZW50IHNldCBvZiBwYWNrYWdlcy4KCklmIHlvdSBqdXN0IHVwZGF0ZWQgYFJgLCB5b3UgY2FuIHNraXAgdGhpcyBzZWN0aW9uLiAgSWYgeW91IGhhdmUgbm90IHVwZGF0ZWQgeW91ciBwYWNrYWdlIGxpc3QgaW4gYSB3aGlsZSwgeW91IG1heSB3YW50IHRvIHVwZGF0ZSBhbnkgb3V0LW9mLWRhdGUgcGFja2FnZXMgeW91IG1heSBoYXZlIG9uIHlvdXIgc3lzdGVtLiAgRG8gdGhpcyB3aGVuIHlvdSBhcmUgbm90IG5lZWRpbmcgdG8gdXNlIGBSYCBmb3IgYSB3aGlsZSwgZGVwZW5kaW5nIHVwb24gaG93IG11Y2ggeW91IG5lZWQgdG8gdXBncmFkZSwgaXQgbWF5IHRha2UgYSB3aGlsZS4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQp1cGRhdGUucGFja2FnZXMoIGFzaz1GQUxTRSApCmBgYAoKSWYgeW91IGRvIG5vdCBwdXQgdGhlIGBhc2s9RkFMU0VgIHBhcnQgaW4gdGhlcmUsIHlvdSdsbCBiZSB0ZXRoZXJlZCB0byB0aGUgY29tcHV0ZXIgYmVjYXVzZSBpdCB3aWxsIGFzayB5b3VyIHBlcm1pc3Npb24gdG8gaW5zdGFsbCBlYWNoIHBhY2thZ2Ugb25lIGF0IGEgdGltZS4uLiAgCgoKIyMgUlN0dWRpbwoKSSBhbSBhc3N1bWluZyB0aGF0IHlvdSBhcmUgdXNpbmcgW1JTdHVkaW9dKGh0dHBzOi8vcG9zaXQuY28pIGZvciB0aGlzIGNvdXJzZS4gIElmIHlvdSBwcmVmZXIgdG8gdXNlIGFub3RoZXIgcGxhdGZvcm0gc3VjaCBhcyBbSnVweXRlciBOb3RlYm9va3NdKGh0dHBzOi8vanVweXRlci5vcmcpLCBbVmlzdWFsIFN0dWRpb10oaHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20pLCBvciBvdGhlciBJREUncywgeW91IGFyZSBvbiB5b3VyIG93bi4gIEhvd2V2ZXIsIHRoaXMgY291cnNlIHdpbGwgcmVxdWlyZSB5b3UgdG8gcHJvZHVjZSBtYXJrZG93biBkb2N1bWVudHMgaW4gSFRNTCBvciBET0NYIGZvcm1hdC4KCklmIHlvdSBkbyBub3QgaGF2ZSB0aGUgbW9zdCByZWNlbnQgdmVyc2lvbiBvZiBSU3R1ZGlvLCBwbGVhc2UgZG93bmxvYWQgaXQgZnJvbSBbaGVyZV0oaHR0cHM6Ly9wb3NpdC5jbykKCgojIyBRdWFydG8KCldlIGFyZSBnb2luZyB0byB1c2UgW1F1YXJ0b10oaHR0cHM6Ly9xdWFydG8ub3JnKSBpbiB0aGlzIGNvdXJzZSB0byBtYWtlIGludGVncmF0ZSBvdXIgZGF0YSBhbmFseXNpcywgZ3JhcGhpY2FsIG91dHB1dCwgcmVwb3J0IHdyaXRpbmcuICBJZiB5b3UgaGF2ZSBub3QgdXNlZCBgcXVhcnRvYCBiZWZvcmUsIHlvdSB3aWxsIG5lZWQgdG8gZG93bmxvYWQgdGhlIG1vc3QgcmVjZW50IHZlcnNpb24uICBUaGVyZSBpcyBhIGNvbXBsZXRlIGRpc2N1c3Npb24gb2YgaG93IHRvIHN0YXJ0IHdpdGggUXVhcnRvIG9uIHRoZSB3ZWJzaXRlLiAgRG93bmxvYWQgdGhlIHZlcnNpb24gZm9yIHlvdXIgY29tcHV0ZSBwbGF0Zm9ybSBhbmQgY2hvb3NlIGBSU3R1ZGlvYCAob3Igd2hhdGV2ZXIgSURFKSB5b3UgYXJlIHVzaW5nLiAgSXQgd2lsbCBzZXQgeW91IHVwLgoKIyBSU3R1ZGlvIFByb2plY3RzCgpgUlN0dWRpb2AgaGFzIHRoZSBjb25jZXB0IG9mIGEgKnByb2plY3QqIGZvciBjb2RlIGFuZCBkYXRhIG9yZ2FuaXphdGlvbi4gIEkgKmhpZ2hseSByZWNvbW1lbmQqIHRoYXQgeW91IHVzZSBhIHNpbmdsZSBwcm9qZWN0IGZvciB0aGlzIGNvdXJzZS4gIFRoaXMgd2lsbCBjby1sb2NhdGUgYWxsIHRoZSBkYXRhLCBhbGwgdGhlIGNvZGUsIGFuZCBhbGwgdGhlIG1hcmtkb3duIHRoYXQgeW91IHdpbGwgYmUgZ2l2ZW4gYW5kIHRoYXQgeW91IGNyZWF0ZSBmb3IgdGhpcyBjbGFzcy4gIEl0IHdpbGwgYWxzbyBzZWVtbGVzc2x5IGhhbmRsZSB3b3JraW5nIGRpcmVjdG9yaWVzIGFuZCBhbGwgdGhlIG90aGVyIGNyYXAgdGhhdCBiZWZ1ZGRsZXMgbWFueSBzdHVkZW50cy4gIFRvIG1ha2UgYSBuZXcgcGFja2FnZSwgb3BlbiB1cCBgUlN0dWRpb2AgYW5kIGRvIHRoZSBmb2xsb3dpbmc6CgoKMS4gRnJvbSB0aGUgYEZpbGVgIG1lbnUsIHNlbGVjdCBgTmV3IFByb2pjdGAKIVtdKG1lZGlhL05ld19Qcm9qZWN0XzEucG5nKQoKMi4gU2VsZWN0IGBOZXcgRGlyZWN0b3J5YCBmcm9tIHRoZSBwb3B1cCBkaWFsb2cuIFRoaXMgd2lsbCBjcmVhdGUgYSBuZXcgZm9sZGVyIHRvIHB1dCBhbGwgeW91ciBtYXRlcmlhbHMgaW50by4KCiFbXShtZWRpYS9OZXdfUHJvamVjdF8yLnBuZykKCgozLiBTZWxlY3QgYE5ldyBQcm9qZWN0YCBmb3IgdGhpcywgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIGdlbmVyaWMgcHJvamVjdCB0byBwdXQgYWxsIHRoZSBjb3Vyc2UgbWF0ZXJpYWxzIGludG8uCgohW10obWVkaWEvTmV3X1Byb2plY3RfMy5wbmcpCgoKNC4gQ3JlYXRlIGEgbmFtZSBmb3IgdGhlIGZvbGRlciwgcGVyaGFwcyBzZWxlY3QgdGhlIG5hbWUgb2YgdGhlIGNsYXNzIG9yIHNvbWUgb3RoZXIgZGVzaWduYXRpb24gdGhhdCB3b3JrcyB3aXRoIHlvdXIgZmlsZSBvcmdhbml6YXRpb24gc3lzdGVtLiAgQWxzbywgcHV0IGl0IHNvbWV3aGVyZSB0aGF0IHdpbGwgYmUgdXNlYWJsZSAoSSBoYWQgYSBzdHVkZW50IHJlY2VudGx5IHdobyBvbmx5IHB1dCBzdHVmZiBpbiB0aGUgYERvd25sb2Fkc2AgZm9sZGVyIGZvciBzb21lIHJlYXNvbi4uLikuICAKCiFbXShtZWRpYS9OZXdfUHJvamVjdF80LnBuZykKCjUuIFNlbGVjdCBgQ3JlYXRlIFByb2plY3RgLgoKTm93LCB3aGVuZXZlciB5b3UgZ2V0IGEgZG9jdW1lbnQgb3IgZGF0YSBmaWxlLCAqcHV0IGl0IGluIHRoaXMgZm9sZGVyKi4gIAoKOjo6ey5jYWxsb3V0LXdhcm5pbmd9Ckl0IGlzIGltcG9ydGFudCBmb3IgbWUgdG8gZW1waGFzaXplIHRoaXMuICBXaGVuIHlvdSBkb3dubG9hZCBhIGRhdGEgc2V0IG9yIG1hcmtkb3duIGRvY3VtZW50IG9yIFIgc2NyaXB0IGFuZCBzaW1wbHkgZG91YmxlIGNsaWNrIG9uIGl0LCBpdCB3aWxsIG1vc3QgbGlrZWx5IGJlIGluIHlvdXIgRG93bmxvYWRzIGZvbGRlci4gIAoKKipNb3ZlIGl0IHRvIHRoZSBwcm9qZWN0IGZvbGRlcioqIHNvIGl0IGlzIGNvLWxvY2F0ZWQgd2l0aCBhbGwgdGhlIG90aGVyIHN0dWZmIGZvciB0aGlzIGNvdXJzZS4gIElmIHlvdSBkbyBub3QsIHNvbWUgb2YgdGhlIGNvZGUgbWF5IG5vdCB3b3JrIHByb3Blcmx5IGFuZCB5b3UnbGwgYmUgaGF0aW5nIHlvdXJzZWxmLiAgKER5ZXIncyAjMSBydWxlLCBkbyB0aGluZ3Mgbm93IHNvICdmdXR1cmUgc2VsZicgZG9lcyBub3QgaGF0ZSBkZWNpc2lvbnMgdGhhdCAncGFzdCBzZWxmJyBtYWRlLi4uKQo6OjoKCk1vcmUgaW1wb3J0YW50bHksIHdoZW4gYFJTdHVkaW9gIG9wZW5zLCBpdCB3aWxsIGRlZmF1bHQgdG8gb3BlbmluZyB0aGUgbW9zdCByZWNlbnQgcHJvamVjdCAod2hpY2ggaW4gYWxsIGxpa2VsaWhvb2Qgd2lsbCBiZSB0aGlzIHByb2plY3QpLiAgCgoKIyBNYXJrZG93bgoKSW4gYFJgLCB3ZSBoYXZlIHR3byBjYXRlZ29yaWVzIG9mIGRvY3VtZW50cyB0aGF0IHdlIHdpbGwgYmUgdXNpbmcuICAKCiMjIGBSYCBTY3JpcHRzCgoKWW91IGNhbiB3cml0ZSBwdXJlIGBSYCBjb2RlIGluIHNjcmlwdHMsIHdoaWNoIGFyZSBmaWxlcyB0aGF0IGFyZSBleGVjdXRlZCBvbmUgbGluZSBhdCBhIHRpbWUsIGlkZW50aWNhbCB0byB5b3UgdHlwaW5nIGl0IGludG8gdGhlIGBSYCBjb25zb2xlLiAgVGhlc2UgZmlsZXMgYXJlIHNhdmVkIHdpdGggdGhlIHN1ZmZpeCBgLlJgLiAgVGhlc2UgYXJlIGdyZWF0IGZvciBwdXJlIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBjb2RpbmcuICAKClRvIGNyZWF0ZSBhbiBgUmAgU2NyaXB0IHNlbGVjdCBgRmlsZWAkXHRvJGBOZXcgRmlsZWAkXHRvJGBSIFNjcmlwdGAuICAKCiFbXShtZWRpYS9SX1NjcmlwdC5wbmcpCgpUbyBleGVjdXRlIGBSYCBjb2RlIGluIGEgc2NyaXB0LCB3ZSBjYW4gc2VsZWN0IGBTb3VyY2VgIG9yIGBSdW5gIGZyb20gdGhlIGJ1dHRvbiBhdCB0b3Agb2YgdGhlIGBSU3R1ZGlvYCBlZGl0b3Igd2luZG93IChqdXN0IGFib3ZlIHRoZSBzY3JpcHQgaXRzZWxmKS4gIE9yLCB5b3UgY2FuIGV4ZWN1dGUgdGhlIGNvZGUgaW4gYSBzY3JpcHQgZnJvbSBhbm90aGVyIGxvY2F0aW9uIChlLmcuLCBmcm9tIGFub3RoZXIgc2NyaXB0KSB1c2luZyB0aGUgZnVuY3Rpb24gYHNvdXJjZSgpYCBhbmQgcGFzc2luZyBpdCB0aGUgcGF0aCAocmVsYXRpdmUgdG8gdGhlIGRvY3VtZW50IHlvdSBhcmUgdXNpbmcgcmlnaHQgbm93KSB0byB0aGUgc2NyaXB0LiAgSXQgd2lsbCB0aGVuIGdyYWIgdGhlIGNvZGUgZnJvbSB0aGUgc2NyaXB0IGFuZCBleGVjdXRlIGl0IGFzIGlmIGl0IHdhcyBwYXJ0IG9mIHRoaXMgc2NyaXB0LiAgCgpgYGB7cn0KI3wgZXZhbDogZmFsc2UKc291cmNlKCJwYXRoX3RvL215X2Nvb2xfc2NyaXB0LlIiKQpgYGAKClRoaXMgaXMgdmVyeSBoZWxwZnVsLCBwYXJ0aWN1bGFybHkgaWYgeW91IHJvdXRpbmVseSBnZXQgcmF3IGRhdGEgdGhhdCBuZWVkcyBzb21lIHByZWxpbWluYXJ5IFFBL1FDLCB5b3UgY2FuIHNldCB1cCBhIHNjcmlwdCB0aGF0IHdpbGwgY2xlYW4gaXQgdXAgYW5kIGNhbGwgaXQgYXMgbmVjZXNzYXJ5LiAgCgo6Ojp7LmNhbGxvdXQtaW5mb30KSXQgaXMgaW1wb3J0YW50IHRvIHRoaW5rIGFib3V0IGNvbXBhcnRtZW5hdGFsaXphdGlvbiBvZiB5b3VyIGNvZGUuICBJZiB5b3UgZXZlciBmaW5kIHlvdXJzZWxmIHR5cGluZyBvciAod29yc2UgeWV0KSBjb3B5LWFuZC1wYXN0aW5nIGNvZGUgeW91J3ZlIHVzZWQgYmVmb3JlIGludG8gYSBuZXcgYW5hbHlzaXPigJRTVE9QLiAgT25lIG9mIG91ciBnb2FscyBpcyB0byB3cml0ZSAqYXMgZmV3IGxpbmVzIG9mIGNvZGUqIGFzIHBvc3NpYmxlLiAgRXJyb3JzIChsb2dpY2FsIGFuZCBncmFtbWF0aWNhbCkgaW5jcmVhc2UgbGluZWFybHkgd2l0aCB0aGUgbnVtYmVyIG9mIGxpbmVzIG9mIGNvZGUgeW91IHdyaXRlLiAgVGhlICpEKm9udCAqUiplcGVhdCAqWSpvdXJzZWxmICgqRFJZKikgcHJpbmNpcGxlIGlzIGtleSB0byBzdWNjZXNzIGluIGRhdGEgYW5hbHl0aWNzLiAgUmVmYWN0b3IgdGhpbmdzIGZvciByZXVzZSB3aWxsIGFsd2F5cyBiZSBiZW5lZml0aWNhbCB0byB5b3UgaW4gdGhlIGxvbmcgcnVuLgo6OjoKCk9uZSBsYXN0IHRoaW5nLiAgV2hlbiB5b3Ugd3JpdGUgY29kZSBpbiBhIHNjcmlwdCwgdXNlIGEgbG90IG9mIGNvbW1lbnRzIHRvIGV4cGxhaW4gd2hhdCB5b3UgYXJlIGRvaW5nIGluIHRoZSBjb2RlLiAgSGVyZSBpcyB0aGUgc3ludGF4IG9mIGEgc2luZ2xlIGxpbmUgY29tbWVudC4KCmBgYHtyfQojIFRoaXMgaXMgYSBjb21tZW50IGFuZCB3aWxsIG5vdCBiZSBpbnRlcnByZXRlZCBieSBSCmBgYAoKJ0Z1dHVyZSBzZWxmJyB3aWxsIHRoYW5rIHlvdSB3aGVuIHlvdSBjb21lIGJhY2sgdG8gdGhpcyBmaWxlIGFuZCB0cnkgdG8gZmlndXJlIG91dCBob3cgdG8gdXNlIGl0IGFnYWluLiAgCgoKCgojIyBRdWFydG8gRG9jdW1lbnRzCgpUaGUgbmV4dCBraW5kIG9mIGZpbGUgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggYXJlIHF1YXJ0byBtYXJrZG93biBkb2N1bWVudHMuICBUaGVzZSBkb2N1bWVudHMgbGV0IHlvdSBtaXggdG9nZXRoZXIgdGhlIGBSYCBjb2RlIChsaWtlIHRoZSBzY3JpcHRzKSwgZ3JhcGhpY2FsIG91dHB1dCwgdGFidWxhciBvdXRwdXQsIGFuZCB0aGUgbGFyZ2VyIG5hcnJhdGl2ZXMgYXJvdW5kIGRhdGEgYW5kIGFuYWx5c2VzIHlvdSBhcmUgY3JlYXRpbmcuICBJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIGxpbmsgdGhlIG51bWVyaWNhbCB2YWx1ZXMgYXNzb2NhaXRlZCB3aXRoIGFuIGFuYWx5c2lzIHRvIGluLXRleHQgdmFsdWVzIHNvIHRoYXQgaWYgdGhlIGRhdGEgY2hhbmdlIHRoZSB0ZXh0IGNoYW5nZXMgYXMgd2VsbC4gIEZpZ3VyZSBhbmQgdGFibGUgbnVtYmVyaW5nIGFuZCBjaXRhdGlvbnMgYXJlIGFsc28gdXBkYXRlZCBhdXRvbWF0aWNhbGx5LiAgSXQgaXMgaW4geW91ciBiZXN0IGludGVyZXN0IChpZiB5b3VyIHRpbWUgaXMgaW1wb3J0YW50IHRvIHlvdSkgdG8gYmVjb21lIHByb2ZpY2llbnQgaW4gaG93IG1hcmtkb3duIGFuZCBgUmAgaW50ZXJhY3QuCgpUaGUgZG9jdW1lbnQgeW91IGFyZSByZWFkaW5nIHJpZ2h0IGhlcmUgaXMgYSBxdWFydG8gZG9jdW1lbnQuICBUaGVzZSBmaWxlcyBlbmQgaW4gdGhlIGZpbGUgc3VmZml4IGAucW1kYC4gIAoKSWYgeW91IGhhdmUgbm90IHVzZWQgbWFya2Rvd24gYmVmb3JlLCB5b3UgbmVlZCB0byBqdW1wIGluIHJpZ2h0IG5vdy4gIEhlcmUgYXJlIHNvbWUgcmVzb3VyY2VzIGZvciB5b3UgZnJvbSBhIERhdGEgTGl0ZXJhY3kgY291cnNlIEkgdGVhY2ggd2hpY2ggd2lsbCBoZWxwIHlvdSBnZXQgdXAtdG8tc3BlZWQgb24gdXNpbmcgTWFya2Rvd24gaW4gUlN0dWRpby4gIAoKLSBbU2xpZGVzXShodHRwczovL2R5ZXJsYWJ0ZWFjaGluZy5naXRodWIuaW8vTWFya2Rvd24vc2xpZGVzLmh0bWwpIGRlc2NyaWJpbmcgdGhlIHJhdGlvbmFsZSBmb3IgTWFya2Rvd24gYW5kIGhvdyB0byB1c2UgaXQgaW4gUlN0dWRpbyB3aGVyZSB5b3UgY2FuIG1peCB0ZXh0dWFsIGNvbnRlbnQgd2l0aCBkYXRhLCBjb2RlLCBhbmQgb3V0cHV0LiAgCi0gQSBsb25nZXIgW25hcnJhdGl2ZV0oaHR0cHM6Ly9keWVybGFidGVhY2hpbmcuZ2l0aHViLmlvL01hcmtkb3duL25hcnJhdGl2ZS5odG1sKSBvbiB0aGUgdG9waWMgdGhhdCBnb2VzIGludG8gbW9yZSBkZXB0aCB0aGFuIHRoZSBzbGlkZXMgdGhhdCBpbmNsdWRlcyBjb2RlIGFuZCBleGFtcGxlIG91dHB1dC4KLSBBIFtWaWRlb10oaHR0cHM6Ly95b3V0dS5iZS9mNTBIejkySXlJUSkgb24gTWFya2Rvd24gYW5kIE5vdGVib29rcyBpbiBSU3R1ZGlvIHRoYXQgSSBtYWRlIGR1cmluZyB0aGUgQ09WSUQgbG9ja2Rvd24gd2hlbiB0ZWFjaGluZyB0aGlzIGNvdXJzZS4KLSBFeHRlcm5hbCByZXNvdXJjZXMgcmVhbHRlZCB0byB1c2luZyBtYXJrZG93bjogIAogIC0gQSBiYXNpYyBbTWFya2Rvd24gQ2hlYXRzaGVldF0oaHR0cHM6Ly93d3cubWFya2Rvd25ndWlkZS5vcmcvY2hlYXQtc2hlZXQvKSAgCiAgLSBBIHNpdGUgYmFzZWQgdXBvbiBbUk1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pIHdpdGggZXhhbXBsZXMuCiAgLSBUaGUgb2ZmaWNpYWwgZG9jdW1lbnRhdGlvbiBmb3IgW1F1YXJ0b10oaHR0cHM6Ly9xdWFydG8ub3JnL2RvY3MvZ3VpZGUvKSAod2hpY2ggaXMgdGhlIG1haW4gaW50ZXJmYWNlIHdlIHdpbGwgYmUgdXNpbmcpLgoKCiMgRGF0YSBNYW5pdXBsYXRpb24KCklmIHdlIGxvb2sgYXQgdGhlIGFtb3VudCBvZiB0aW1lIHdlIGFsbG9jYXRlIHRvIG91ciBkYXRhIGFuYWx5dGljcywgdGhlIHZhc3QgbWFqb3JpdHkgb2YgaXQgaXMgc3BlbnQgb24gd29ya2luZyBvbiB0aGUgZGF0YSwgbWFuaXB1bGF0aW5nIGl0LCBnZXR0aW5nIGl0IGludG8gYSBmb3JtYXQgdGhhdCBpcyB1c2FibGUsIGFuZCBhIHZlcnkgc21hbGwgZnJhY3Rpb24gb2YgaXQgaXMgc3BlbnQgb24gcnVubmluZyBhbiBhY3R1YWwgYW5hbHlzaXMuICBVc3VhbGx5LCBieSB0aGUgdGltZSB3ZSBnZXQgdG8gdGhlIGFuYWx5c2lzLCB3ZSBhcmUgZG9pbmcgYSBzaW1wbGUgZnVuY3Rpb24gY2FsbCB3aXRoIG91ciBmb3JtYXR0ZWQgZGF0YS4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQphbmFseXNpcyggbXlEYXRhICkKYGBgCgpJIGFtIGhvcGluZyB0aGF0IHlvdSBhcmUgdmVyc2VkIGluIHVzaW5nIGB0aWR5dmVyc2VgIGZvciBkYXRhIG1hbmlwdWxhdGlvbi4gIFRoaXMgaXMgYSBjb25zdGVsbGF0aW9uIG9mIHBhY2thZ2VzIHRoYXQgaGVscCB5b3UgdGFrZSByYXcgZGF0YSAoZ2Vub3R5cGVzLCByYXN0ZXJzLCBzaGFwZWZpbGVzLCBjc3YgZmlsZXMsIGV0Yy4pIGFuZCBwZXJmb3JtIG9wZXJhdGlvbnMgb24gaXQuICBMb2FkaW5nIGluIGB0aWR5dmVyc2VgIGlzIGRvbmUgYnkgY2FsbGluZyB0aGUgbGlicmFyeSBkaXJlY3RseS4KCmBgYHtyfQpsaWJyYXJ5KCB0aWR5dmVyc2UgKQpgYGAKCklmIHlvdSBhcmUgZ2V0dGluZyBhICJsaWJyYXJ5IG5vdCBmb3VuZCIgZm9yIHRoaXMsIGluc3RhbGwgaXQgdXNpbmcgdGhlIGZvbGxvd2luZy4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQppbnN0YWxsLnBhY2thZ2VzKCAidGlkeXZlcnNlIiApCmBgYAoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIGhvdyBpdCBjYW4gYmUgdXNlZCB0byBzdW1tYXJpemUgZGF0YSBmcm9tIHRoZSBidWlsdC1pbiAqSXJpcyogZGF0YSBzZXQuCgpgYGB7cn0Kc3VtbWFyeSggaXJpcyApCmBgYAoKVGhlIGZvbGxvd2luZyBjb2RlIGRvZXMgdGhlIGZvbGxvd2luZzogIAotIG91dHB1dHMgdGhlIHJhdyBkYXRhLiAKLSBjcmVhdGVzIGEgbmV3IGNvbHVtbiBvZiBkYXRhIHdpdGggZ2VudXMgYW5kIHNwZWNpZXMuIAotIGdyb3VwcyBieSBzcGVjaWVzLiAKLSBzdW1tYXJpemVzIGJ5IHRha2luZyB0aGUgbWVhbiB2YWx1ZXMgZm9yIHBldGFsIGxlbmd0aCBhbmQgd2lkdGguIAotIHNhdmVzIGFzIGEgYGRhdGEuZnJhbWVgIHRvIGEgbG9jYWwgdmFyaWFibGUuICAKCmBgYHtyfQppcmlzIHw+CiAgbXV0YXRlKCBQbGFudCA9IHBhc3RlKCAiSXJpcyIsU3BlY2llcykpIHw+CiAgZ3JvdXBfYnkoIFBsYW50ICkgfD4KICBzdW1tYXJpemUoIExlbmd0aCA9IG1lYW4oIFBldGFsLkxlbmd0aCApLCAKICAgICAgICAgICAgIFdpZHRoID0gbWVhbiggUGV0YWwuV2lkdGggKSApIC0+IGlyaXNfc3VtbWFyeQpgYGAKCkFuZCB0aGlzIGlzIHdoYXQgdGhlIG91dHB1dCBsb29rcyBsaWtlLgoKYGBge3J9CmlyaXNfc3VtbWFyeSAKYGBgCgpJZiB5b3Ugd2VyZSB1bnN1cmUgd2hhdCB0aGUgb3V0cHV0IG9mIHRoZSBjb2RlIHdhcyBnb2luZyB0byBsb29rIGxpa2Ugb3IgZG8gbm90IHVuZGVyc3RhbmQgd2hhdCB3YXMgZG9uZSwgeW91IG1heSBuZWVkIHRvIHdvcmsgYSBiaXQgb24gZ2V0dGluZyB1cCB0byBzcGVlZCBvbiBgdGlkeXZlcnNlYC4KCkhlcmUgYXJlIHNvbWUgcmVzb3VyY2VzIG9uIHVzaW5nIGB0aWR5dmVyc2VgIGZyb20gYSBmZXcgZGlmZmVyZW50IHNvdXJjZXMuICAKCiAtIEEgW1ZpZGVvXShodHRwczovL3lvdXR1LmJlL2RVWnlPaFZHT2pvP3NpPUJ2YnpuSWV1YjdEX2NITlgpIGludHJvZHVjaW5nIGB0aWR5dmVyc2VgIGdpdmVuIGZyb20gYSBkaXN0cmlidXRlZCB3b3Jrc2hvcCBvbiBMYW5kc2NhcGUgR2VuZXRpY3MuICAKIC0gVXBkYXRlZCBbU2xpZGVzXShodHRwczovL2R5ZXJsYWJ0ZWFjaGluZy5naXRodWIuaW8vVGlkeXZlcnNlL3NsaWRlcy5odG1sIy90aXRsZS1zbGlkZSkgb2YgdGhlIGNvbnRlbnQuICBUaGVzZSBhcmUgdXBkYXRlZCBmcm9tIHRoZSBzZXQgaW4gdGhlIHZpZGVvIGFzIHRoZXkgYXJlIHRoZSBvbmVzIEkgdXNlZCBsYXN0IHNlbXNldGVyLiAgCiAtIEEgbGFyZ2VyIFtuYXJyYXRpdmVdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9UaWR5dmVyc2UvbmFycmF0aXZlLmh0bWwpIGdvaW5nIGludG8gbW9yZSBkZXB0aCBvbiB0aGUgc3BlY2lmaWNzIG9mIHRoZSB0b3BpYy4gIAogLSBUaGUgZGF0YSBzZXQgd2Ugd2lsbCB1c2UgZm9yIHRoaXMgaXMgdGhlIFJpY2UgUml2ZXJzIENlbnRlciBkYXRhIHNldCAoZnJvbSAyMDE0KS4gIFlvdSBjYW4gc2VlIGl0IGhlcmUgYXMgaXRzIG5vcm1hbCBbR29vZ2xlIFNoZWV0c10oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMU1rMVlHSDlMcWpGN2RySkUtdGQxR19Ka2RBRE9VMGVNbHJQMDFXRkJUOHMvZWRpdD91c3A9c2hhcmluZykgb3IgYXMgYSBbQ1NWXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xTWsxWUdIOUxxakY3ZHJKRS10ZDFHX0prZEFET1UwZU1sclAwMVdGQlQ4cy9wdWI/Z2lkPTAmc2luZ2xlPXRydWUmb3V0cHV0PWNzdikgZmlsZS4gIAogLSBBIFtjaGVhdCBzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS10cmFuc2Zvcm1hdGlvbi5wZGYpIGdvaW5nIG92ZXIgdGhlIGJhc2ljIGFuYWx5c2lzIHZlcmIgZnVuY3Rpb25zIGZyb20gYGRwbHlyYC4gIAoKCiMgR3JhcGhpY3MKClBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgIGlzIHRoZSBgZ2dwbG90MmAgbGlicmFyeS4gIFRoaXMgaXMgdGhlIHByaW1hcnkgYW5kIGRvbWluYW50IGdyYXBoaW5nIG91dHB1dCBmb3IgYFJgIGFuZCB3aWxsIGJlIHVzZWQgdGhyb3VnaG91dCB0aGlzIGNvdXJzZS4gIFlvdSBzaG91bGQgYmUgZmFtaWxpYXIgd2l0aCB0aGUgY29kZSBzZXRzIGJlbG93IChlbm91Z2ggdG8gdW5kZXJzdGFuZCB3aGF0IHRoZXkgYXJlIGRvaW5nKSBhbmQgYmUgYWJsZSB0byBwcm9kdWNlIGJhc2ljIGRhdGEgZ3JhcGhpY3MuCgpgYGB7cn0KI3wgZWNobzogZmFsc2UgCnRoZW1lX3NldCggdGhlbWVfbWluaW1hbCgpICkKCmBgYAoKCkEgYmFzaWMgc2NhdHRlciBwbG90IHdpdGggZ3JvdXBpbmdzIGFuZCBhIHRyZW5kIGxpbmUgKEBmaWctc2NhdHRlcikuCgpgYGB7cn0KI3wgbGFiZWw6IGZpZy1zY2F0dGVyCiN8IGZpZy1jYXA6ICJUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcGV0YWwgbGVuZ3RoIGFuZCB3aWR0aCBmb3IgdGhyZWUgaXJpcyBzcGVjaWVzIChpbmRpY2F0ZWQgYnkgYWx0ZXJuYXRpdmUgY29sb3JzKSB3aXRoIGxpbmVhciB0cmVuZGxpbmUgKGluIGdyYXkpLiIKCmlyaXMgfD4gCiAgZ2dwbG90KCBhZXMoUGV0YWwuV2lkdGgsIFBldGFsLkxlbmd0aCApICkgKyAKICBzdGF0X3Ntb290aCggZm9ybXVsYSA9IHl+eCwKICAgICAgICAgICAgICAgbWV0aG9kPSJsbSIsCiAgICAgICAgICAgICAgIHNlPUZBTFNFLCBjb2w9ImdyYXkiLCAKICAgICAgICAgICAgICAgbHdkPTAuNSwgbHR5PTIgKSArIAogIGdlb21fcG9pbnQoIGFlcyggY29sb3IgPSBTcGVjaWVzICkgKSArCiAgeGxhYigiUGV0YWwgV2lkdGggKG1tKSIpICsKICB5bGFiKCJQZXRhbCBMZW5ndGggKG1tKSIpCmBgYAoKCkEgY2F0ZWdvcmljYWwgZGF0YSBvdXRwdXQgdXNpbmcgYGJveHBsb3QoKWAgKEBmaWctYm94cGxvdCkgb3IgYHZpb2xpbigpYCAoQGZpZy12aW9saW4pIGRpc3BsYXkgdG8gY29tcGFyZSBjb250aW51b3VzIHJlc3BvbnNlcyBmcm9tIGZhY3RvcnMuCgoKYGBge3J9CiN8IGxhYmVsOiBmaWctYm94cGxvdAojfCBmaWctY2FwOiAiQSBib3hwbG90IHBsb3Qgb2Ygc2VwYWwgbGVuZ3RoIGZvciB0aHJlZSBpcmlzIHNwZWNpZXMuIgoKaXJpcyB8PiAKICBnZ3Bsb3QoIGFlcyhTcGVjaWVzLFBldGFsLkxlbmd0aCkgKSArIAogIGdlb21fYm94cGxvdChub3RjaD1UUlVFKSArICAKICB4bGFiKCJJcmlzIHNwZWNpZXMiKSArCiAgeWxhYigiUGV0YWwgTGVuZ3RoIChtbSkiKQpgYGAKCgpgYGB7cn0KI3wgbGFiZWw6IGZpZy12aW9saW4KI3wgZmlnLWNhcDogIkEgdmlvbGluIHBsb3Qgb2Ygc2VwYWwgbGVuZ3RoIG92ZXJsYWluIHdpdGggaW5kaXZpZHVhbCBtZWFzdXJlbWVudHMgZm9yIHRocmVlIGlyaXMgc3BlY2llcy4iCgppcmlzIHw+IAogIGdncGxvdCggYWVzKFNwZWNpZXMsUGV0YWwuTGVuZ3RoKSApICsgCiAgZ2VvbV92aW9saW4oKSArIAogIGdlb21faml0dGVyKCB3aWR0aCA9IDAuMDUsIGFscGhhID0gMC41KSArCiAgeGxhYigiSXJpcyBzcGVjaWVzIikgKwogIHlsYWIoIlBldGFsIExlbmd0aCAobW0pIikKYGBgCgpBIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdmFyaWFibGVzIChAZmlnLWRlbnNpdHkpIGFjcm9zcyBkaWZmZXJlbnQgZ3JvdXBzLgoKYGBge3J9CiN8IGxhYmVsOiBmaWctZGVuc2l0eQojfCBmaWctY2FwOiAiUHJvYmFiaWxpdHkgZGVuc2l0eSBvZiBwZXRhbCBsZW5ndGggZm9yIHRocmVlIGlyaXMgc3BlY2llcy4iCmlyaXMgfD4KICBnZ3Bsb3QoIGFlcyhTZXBhbC5MZW5ndGgsIGZpbGw9U3BlY2llcykgKSArIAogIGdlb21fZGVuc2l0eSggYWxwaGEgPSAwLjc1LCBjb2w9ImRhcmtncmF5IikgKwogIHhsYWIoIklyaXMgc3BlY2llcyIpICsKICB5bGFiKCJQZXRhbCBMZW5ndGggKG1tKSIpICsgCiAgeGxpbSggYygzLDkpICkKYGBgCgpJZiB0aGVzZSBtZXRob2RzIGFyZSBuZXcgdG8geW91LCBoZXJlIGFyZSBzb21lIHJlc291cmNlcyB0aGF0IHdpbGwgaGVscC4KCiAtIFNsaWRlcyBmb3IgW2NsYXNzaWMgcGxvdHRpbmddKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL3NsaWRlc19jbGFzc2ljLmh0bWwpIGFuZCBmb3IgW2dncGxvdDJdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL3NsaWRlcy5odG1sKSBwbG90dGluZy4KIC0gVGhlIG5hcnJhdGl2ZSBmb3IgYm90aCBbY2xhc3NpYyBwbG90dGluZ10oaHR0cHM6Ly9keWVybGFidGVhY2hpbmcuZ2l0aHViLmlvL0dyYXBoaWNzLVRoYXQtRG8tTm90LVN1Y2svbmFycmF0aXZlX2NsYXNzaWMuaHRtbCkgYXMgd2VsbCBhcyBmb3IgW2dncGxvdDJdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL25hcnJhdGl2ZS5odG1sKSBncmFwaGljcwogLSBbSW4gQ2xhc3NdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL2luLWNsYXNzLmh0bWwpIGRvY3VtZW50cy4KIC0gRXh0ZXJuYWwgcmVzb3VyY2VzOgogICAtIFtEYXRlIHRvIFZpel0oaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tKS4KICAgLSBbUiBHcmFwaCBHYWxsZXJ5XShodHRwczovL3ItZ3JhcGgtZ2FsbGVyeS5jb20vZ2dwbG90Mi1wYWNrYWdlLmh0bWwpCiAgIC0gW0dHUGxvdDIgQ2hlYXRzaGVldF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24ucGRmKQoKIyBUYWJ1bGFyIE91dHB1dAoKVGhlIGxhc3QgcGFydCBvZiB0aGlzIHJldmlldyB3aWxsIGZvY3VzIG9uIHRhYnVsYXIgb3V0cHV0LiAgTWFueSB0aW1lcywgd2UgaGF2ZSBkYXRhIHdlIG5lZWQgdG8gc3VtbWFyaXplIChzZWUgYWJvdmUgdGhlIGB0aWR5dmVyc2VgIGV4YW1wbGUgZGF0YSB0aGF0IHByb2R1Y2VkIGEgYGRhdGEuZnJhbWVgIG9mIG1lYW4gcGV0YWwgc2l6ZXMpIGFuZCB3b3VsZCBsaWtlIHRvIGNyZWF0ZSBhIHRhYmxlIGluIG91ciBkb2N1bWVudCB0aGF0IHdpbGwgcHJlc2VudCB0aGUgcmVzdWx0cyB0byB0aGUgcmVhZGVyIChvciBhbiBBTk9WQSB0YWJsZSBvciB3aGF0ZXZlcikuICBUaGUgbW9zdCBjb21tb24gYXBwcm9hY2ggaW4gYFJgIGlzIHRvIHVzZSB0aGUgZm9sbG93aW5nOgoKYGBge3J9CiN8IG1lc3NhZ2VzOiBmYWxzZQpsaWJyYXJ5KCBrbml0ciApCmxpYnJhcnkoIGthYmxlRXh0cmEgKQpgYGAKCkhlcmUgaXMgdGhlIGRhdGEgZnJvbSBhYm92ZToKCmBgYHtyfQppcmlzX3N1bW1hcnkKYGBgCgpXZSBjYW4gbWFrZSB0aGlzIGludG8gYSB0YWJsZSBmb3IgcHVibGljYXRpb24gb3V0cHV0IGFzIGlzIChAdGJsLWlyaXMpLgoKYGBge3J9CiN8IGxhYmVsOiB0YmwtaXJpcwojfCB0YmwtY2FwOiAiTWVhbiBkaW1lbnNpb25zIGZvciBwZXRhbHMgc2FtcGxlZCBmcm9tIDUwIGluZGl2aXVkYWxzIGluIGVhY2ggaXJpcyBzcGVjaWVzLiIKaXJpc19zdW1tYXJ5IHw+CiAga2FibGUoKSB8PgogIGthYmxlX3N0eWxpbmcoIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpICkgfD4KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIiAiID0gMSwgIk1lYW4gUGV0YWwgTWVhc3VyZW1lbnQgKG1tKSI9MikgKSB8PgogIGZvb3Rub3RlKGdlbmVyYWw9Ik4gPSA1MCBwZXIgc3BlY2llcy4iLCBmb290bm90ZV9hc19jaHVuayA9IFRSVUUpCiAKYGBgCgpUaGVyZSBpcyBhIGdyZWF0IHdlYnBhZ2UgZm9yIGBrYWJsZUV4dHJhYCB0aGF0IGRlc2NyaWJlcyBhIGxvdCBvZiBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdHkgZm9yIHRoaXMgbGlicmFyeSBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2thYmxlRXh0cmEvdmlnbmV0dGVzL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sKS4gIEkgcmVjb21tZW5kIHlvdSB0YWtlIGEgbG9vayAgYXQgaXQuCgojIENvbmNsdXNpb24KClNvIHRoaXMgc2hvdWxkIGJlIHN1ZmZpY2llbnQgZm9yIHdoYXQgd2UgbmVlZCBpbiB0aGlzIGNvdXJzZS4gIElmIHlvdSBoYXZlIGFueSBjb25jZXJucyBhYm91dCB5b3VyIGFiaWxpdHkgdG8gZG8gYW55IG9mIHRoZSBhYm92ZSwgcGxlYXNlIGxldCBtZSBrbm93IGFuZCB3ZSBjYW4gZ2V0IHRvIHdvcmsgb24gaGVscGluZyB5b3UgZ2V0IHRvIHdoZXJlIHlvdSBuZWVkIHRvIGJlLgoKSWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucywgZmVlbCBmcmVlIHRvIGRyb3AgYnkgbXkgb2ZmaWNlIChMRlNDIDEwNWMpLCBqdW1wIG9udG8gbXkgT2ZmaWNlIEhvdXJzIFpvb20gKGxpbmtlZCBmb3JtIFtDYW52YXNdKGh0dHBzOi8vY2FudmFzLnZjdS5lZCkpLCBvciBzZW5kIG1lIGFuIFtlbWFpbF0obWFpbHRvOi8vcmpkeWVyQHZjdS5lZHUpLgoKLSBEckQKCgoKCgoKCgoKCgoKCgoKCgo=